﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

namespace DotNetExtensions.AspNet.DevExpress.WIN
{
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct BLENDFUNCTION
    {
        /// <summary>
        /// BlendOp field of structure
        /// </summary>
        public byte BlendOp;

        /// <summary>
        /// BlendFlags field of structure
        /// </summary>
        public byte BlendFlags;

        /// <summary>
        /// SourceConstantAlpha field of structure
        /// </summary>
        public byte SourceConstantAlpha;

        /// <summary>
        /// AlphaFormat field of structure
        /// </summary>
        public byte AlphaFormat;
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct POINT
    {
        /// <summary>
        /// x field of structure
        /// </summary>
        public int x;

        /// <summary>
        /// y field of structure
        /// </summary>
        public int y;

        #region Constructors
        public POINT(int x, int y)
        {
            this.x = x;
            this.y = y;
        }

        public POINT(Point point)
        {
            x = point.X;
            y = point.Y;
        }
        #endregion
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct RECT
    {
        /// <summary>
        /// left field of structure
        /// </summary>
        public int left;

        /// <summary>
        /// top field of structure
        /// </summary>
        public int top;

        /// <summary>
        /// right field of structure
        /// </summary>
        public int right;

        /// <summary>
        /// bottom field of structure
        /// </summary>
        public int bottom;

        public RECT(int left, int top, int width, int height)
        {
            this.left = left;
            this.top = top;
            right = this.left + width;
            bottom = this.top + height;
        }

        #region Properties

        public POINT Location
        {
            get
            {
                return new POINT(left, top);
            }
            set
            {
                right -= (left - value.x);
                bottom -= (bottom - value.y);
                left = value.x;
                top = value.y;
            }
        }

        public uint Width
        {
            get { return (uint)Math.Abs(right - left); }
            set { right = left + (int)value; }
        }

        public uint Height
        {
            get { return (uint)Math.Abs(bottom - top); }
            set { bottom = top + (int)value; }
        }
        #endregion

        #region Overrides

        public override string ToString()
        {
            return left + ":" + top + ":" + right + ":" + bottom;
        }

        #endregion

        public static explicit operator Rectangle(RECT rect)
        {
            return new Rectangle(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct SIZE
    {
        /// <summary>
        /// cx field of structure
        /// </summary>
        public int cx;

        /// <summary>
        /// cy field of structure
        /// </summary>
        public int cy;

        public SIZE(Int32 cx, Int32 cy)
        {
            this.cx = cx;
            this.cy = cy;
        }
    }
    public class Win32
    {
        [DllImport("user32.dll")]
        public static extern bool MoveWindow(IntPtr hWnd, int x, int y, int width, int height, bool repaint);

        [DllImport("user32.dll")]
        public static extern bool ReleaseCapture();

        [DllImport("user32.dll")]
        public static extern bool SendMessage(IntPtr hwnd, uint wMsg, uint wParam, long lParam);

        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        public static extern bool GetWindowRect(IntPtr hWnd, ref RECT rect);

        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr GetDC(IntPtr hWnd);

        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        public static extern bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst, ref POINT pptDst, ref SIZE psize, IntPtr hdcSrc, ref POINT pprSrc, Int32 crKey, ref BLENDFUNCTION pblend, Int32 dwFlags);

        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDc);
        [DllImport("gdi32.dll")]
        public static extern IntPtr CreateSolidBrush([In] uint color);

        [DllImport("gdi32.dll")]
        public static extern bool Rectangle(IntPtr hdc, int nLeftRect, int nTopRect, int nRightRect, int nBottomRect);

        [DllImport("gdi32.dll", EntryPoint = "SelectObject", SetLastError = true)]
        public static extern IntPtr SelectObject([In] IntPtr hdc, [In] IntPtr hgdiobj);

        [DllImport("gdi32.dll", EntryPoint = "DeleteObject")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool DeleteObject([In] IntPtr hObject);

        [DllImport("gdi32.dll", EntryPoint = "CreateCompatibleDC", SetLastError = true)]
        public static extern IntPtr CreateCompatibleDC([In] IntPtr hdc);

        [DllImport("gdi32.dll", EntryPoint = "DeleteDC")]
        public static extern bool DeleteDC([In] IntPtr hdc);
    }

    public static class WinformExtensions
    {
        #region Resize form
        const int WM_NCHITTEST = 0x0084;
        const int HTLEFT = 10;      //左边界
        const int HTRIGHT = 11;     //右边界
        const int HTTOP = 12;       //上边界
        const int HTTOPLEFT = 13;   //左上角
        const int HTTOPRIGHT = 14;  //右上角
        const int HTBOTTOM = 15;    //下边界
        const int HTBOTTOMLEFT = 0x10;    //左下角
        const int HTBOTTOMRIGHT = 17;     //右下角
        public static void WndResize(this Form form, ref Message m, int range = 5)
        {
            switch (m.Msg)
            {
                case WM_NCHITTEST:
                    Point point = new Point((int)m.LParam & 0xFFFF,
                        (int)m.LParam >> 16 & 0xFFFF);
                    point = form.PointToClient(point);
                    if (point.X <= range)
                        if (point.Y <= range)
                            m.Result = (IntPtr)HTTOPLEFT;
                        else if (point.Y >= form.ClientSize.Height - range)
                            m.Result = (IntPtr)HTBOTTOMLEFT;
                        else m.Result = (IntPtr)HTLEFT;
                    else if (point.X >= form.ClientSize.Width - range)
                        if (point.Y <= range)
                            m.Result = (IntPtr)HTTOPRIGHT;
                        else if (point.Y >= form.ClientSize.Height - range)
                            m.Result = (IntPtr)HTBOTTOMRIGHT;
                        else m.Result = (IntPtr)HTRIGHT;
                    else if (point.Y <= range)
                        m.Result = (IntPtr)HTTOP;
                    else if (point.Y >= form.ClientSize.Height - range)
                        m.Result = (IntPtr)HTBOTTOM;
                    break;
            }
        }

        #endregion

        #region NonBorder Form Draggable

        const uint WM_SYSCOMMAND = 0x112;
        const uint SC_MOVE = 0xF010;
        const uint HTCAPTION = 2;
        public static void Moveable(this Form form)
        {
            bool isMouseDown = false;
            var mousedown = new MouseEventHandler((sender, e) =>
            {
                isMouseDown = true;
            });

            var moving = new MouseEventHandler((sender, e) =>
            {
                if (isMouseDown && e.Button == MouseButtons.Left)
                {
                    Win32.ReleaseCapture();
                    Win32.SendMessage(form.Handle, WM_SYSCOMMAND, SC_MOVE + HTCAPTION, 0);
                }
            });

            var mouseup = new MouseEventHandler((sender, e) =>
            {
                isMouseDown = false;
            });

            form.MouseDown += mousedown;
            form.MouseMove += moving;
            form.MouseUp += mouseup;
        }

        public static void AddMoveable(this Form form, Control[] controls)
        {
            bool isMouseDown = false;
            var mousedown = new MouseEventHandler((sender, e) =>
            {
                isMouseDown = true;
            });

            var moving = new MouseEventHandler((sender, e) =>
            {
                if (isMouseDown && e.Button == MouseButtons.Left)
                {
                    Win32.ReleaseCapture();
                    Win32.SendMessage(form.Handle, WM_SYSCOMMAND, SC_MOVE + HTCAPTION, 0);
                }
            });

            var mouseup = new MouseEventHandler((sender, e) =>
            {
                isMouseDown = false;
            });

            foreach (var item in controls)
            {
                item.MouseDown += mousedown;
                item.MouseMove += moving;
                item.MouseUp += mouseup;
            }
        }

        #endregion
    }
}
